package cs2340.g21.wms;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.io.Serializable;
import java.io.IOException;

import android.util.Log;

@SuppressWarnings("serial")

/**
 * This class serves as a sort of database.
 * Currently keeps track of accounts, emails, currently logged in user,
 * id numbers, items, and notes
 * @author Alexander
 * @version 1.6
 */
public class Coordinator extends IOException implements Serializable{
    private static final String LOG_TAG = "debugger";

    private Hashtable<Integer, User> accounts;
    private User user = null;
    private int currentUserId = 0;
    private int currentItemId = 0;
    private Hashtable<String, Integer> emails;
    private Hashtable<Integer, Item> items;
    private Hashtable<Item, Integer> itemIds;
    private List<Note> adminNotes;
    
    public Coordinator(){
        accounts = new Hashtable<Integer,User>();
        emails = new Hashtable<String,Integer>();
        items = new Hashtable<Integer,Item>();
        addAdmin("admin@wms.com", "admin");
        addAccount("user@wms.com", "user");
        itemIds = new Hashtable<Item,Integer>();
        adminNotes = new ArrayList<Note>();
    }
    /**
     * @param email of user to be added
     * @param pass of user to be added
     * @precondition email has not already been used
     */
    public void addAccount(String email, String pass){
        User u = new User(email, pass, nextUserId());
        accounts.put(u.getId(), u);
        emails.put(u.getEmail(), u.getId());
        Log.i(LOG_TAG, "uid:"+ u.getId());
    }
    
    /**
     * @param email of the new admin account
     * @param pass of the new admin account
     */
    public void addAdmin(String email, String pass){
        User u = new User(email,pass,nextUserId());
        u.setAdmin(true);
        accounts.put(u.getId(), u);
        emails.put(u.getEmail(), u.getId());
    }
    
    /**
     * @param email of user to remove
     */
    public void removeAccount(String email){
        accounts.remove(emails.get(email));
        emails.remove(email);
    }
    
    /**
     * @param item to add to storage
     */
    public void addItem(Item item){
        item.setId(nextItemId());
        user.addItem(item);
        items.put(item.getId(), item);
    }
    
    /**
     * @param id of user account to look up
     * @return an instance of user corresponding to that id, null if not found
     */
    public User getAccount(int id){
        return accounts.get(id);
    }
    
    /**
     * @return the list of all items
     */
    public List<Item> getItems(){
        List<Item> i = new ArrayList<Item>();
        for(Item j : items.values()){
            i.add(j);
        }
        return i;
    }
    
    /**
     * @param email to try to find in the used emails list
     * @return true if email not found, false otherwise
     */
    public boolean checkEmailAvailable(String email){
        if(email == null){
            return false;
        }
        if(emails.keySet() == null){
            return false;
        }
        return !emails.keySet().contains(email);
    }
    
    /**
     * @param email email for which to get an id
     * @return id of email if it exists, -1 otherwise
     */
    public int getAccountId(String email){
        if(email == null){
            return -1;
        }
        return emails.get(email);
    }
    
    /**
     * @param item item for which to get id
     * @return id of item if it exists, -1 otherwise
     */
    public int getItemId(Item item){
        if(item == null){
            return -1;
        }
        return itemIds.get(item);
    }
    
    /**
     * @param user to be set as this session's user
     */
    public void setUser(String email){
        this.user = accounts.get(emails.get(email));
    }
    
    /**
     * Resets the user (for use when signing out)
     */
    public void resetUser(){
        this.user = null;
    }
    
    /**
     * @return the currently logged in user
     */
    public User getUser(){
        return user;
    }
    
    /**
     * @return the next user id to be used
     */
    private int nextUserId(){
        return ++currentUserId;
    }
    
    /**
     * @return the next item id 
     */
    private int nextItemId(){
        return ++currentItemId;
    }
    
    /**
     * @return the list of admin notes
     */
    public List<Note> getAdminNotes() {
        return adminNotes;
    }
    
    /**
     * sets the list of admin notes
     * @param adminNotes new list to set admin list to
     */
    public void setAdminNotes(List<Note> adminNotes) {
        this.adminNotes = adminNotes;
    }
    
    /**
     * Adds a note to the master admin note list
     * @param note to add to list
     */
    public void addAdminNote(Note note){
        adminNotes.add(note);
    }
    
    /**
     * Used to serialize coordinator
     * @param out outputstream to which coordinator will be written
     * @throws IOException
     */
    private void writeObject(java.io.ObjectOutputStream out) throws IOException{
            out.defaultWriteObject();   
    }
    
    /**
     * Used to recreate coordinator from an output stream
     * @param in inputstream from which coordinator will be read
     * @throws IOException
     * @throws ClassNotFoundException
     */
    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException{
        in.defaultReadObject();
    }
    
    /**
     * Checks an item against all possible matches and populates match lists if they're found
     * @param item for which to check against rest of items for matches
     * @return true if at least one match was found, false otherwise
     */
    public boolean checkMatch(Item item) {
    	int trueCheck = 0;
    	String itemName = item.getName();
    	String itemLoc;
    	String compareItemName;
    	String compareItemLoc;

        for( Item compareItem : getItems() ){
        	compareItemName = compareItem.getName();
        	if	( 	( ( item.getCategory() == Category.LOST )&&( compareItem.getCategory() == Category.DONATE ) )||
        			( ( item.getCategory() == Category.LOST )&&( compareItem.getCategory() == Category.FOUND ) )||
        			( ( item.getCategory() == Category.FOUND )&&( compareItem.getCategory() == Category.LOST ) )||
        			( ( item.getCategory() == Category.DONATE )&&( compareItem.getCategory() == Category.LOST ) ) ) {
     
      
        		if ( ( item.getLocation() != null )&&( compareItem.getLocation() != null ) ){
        			itemLoc = item.getLocation().getLocality();
        			compareItemLoc = compareItem.getLocation().getLocality();
        			if( ( itemLoc.equals( compareItemLoc ) )&&( itemName.equals(compareItemName) ) ){
        				item.addMatch(compareItem);
        				compareItem.addMatch(item);
        				trueCheck++;
        				//Log.d("debugging", "Location matched "+item.getName() + " and "+compareItem.getName());
        			}
        		}
        		else if( itemName.equals(compareItemName)){
        			item.addMatch(compareItem);
        			compareItem.addMatch(item);
        			trueCheck++;
        		}
        		//Log.d("debugging", item.getName() + " match " + compareItemName );
        	}
        	//Log.d("debugging", i.getName()+" Location: "+i.getLocation().getLocality().toString());
        	//Log.d("debugging", j.getName()+" Location: "+j.getLocation().getLocality().toString());
        	
        }
    	return trueCheck > 0;
    }
    
    /**
     * A method to get all items matched to a user's items
     * @param u user to build a matchlist for
     * @return all items matched with the items of user
     */
    public List<Item> getMatchList(User u){
        ArrayList<Item> matches = new ArrayList<Item>();
        for(Item i : u.getItems()){
            for(Item j : i.getMatches()){
                if(!matches.contains(j)){
                    Log.d(LOG_TAG, "added a match");
                    matches.add(j);
                    
                }
            }
        }
        return matches;
    }
}
